#pragma once

#include <memory>
#include <windows.h>
#include "SharedMemory.h"
#include "DesktopRegion.h"

class DesktopFrame
{
public:
	static const int kBytesPerPixel = 4;

	virtual ~DesktopFrame();

	DesktopRect rect() const;

	float scale_factor() const;

	const DesktopSize& size() const { return size_; }

	const DesktopVector& top_left() const { return top_left_; }
	void set_top_left(const DesktopVector& top_left) { top_left_ = top_left; }

	int stride() const { return stride_; }

	uint8_t* data() const { return data_; }

	SharedMemory* shared_memory() const { return shared_memory_; }

	const DesktopRegion& updated_region() const { return updated_region_; }
	DesktopRegion* mutable_updated_region() { return &updated_region_; }

	const DesktopVector& dpi() const { return dpi_; }
	void set_dpi(const DesktopVector& dpi) { dpi_ = dpi; }

	int64_t capture_time_ms() const { return capture_time_ms_; }
	void set_capture_time_ms(int64_t time_ms) { capture_time_ms_ = time_ms; }

	void CopyPixelsFrom(const uint8_t* src_buffer,
		int src_stride,
		const DesktopRect& dest_rect);
	void CopyPixelsFrom(const DesktopFrame& src_frame,
		const DesktopVector& src_pos,
		const DesktopRect& dest_rect);

	uint8_t* GetFrameDataAtPos(const DesktopVector& pos) const;

	uint32_t capturer_id() const { return capturer_id_; }
	void set_capturer_id(uint32_t capturer_id) { capturer_id_ = capturer_id; }

	void CopyFrameInfoFrom(const DesktopFrame& other);

	void MoveFrameInfoFrom(DesktopFrame* other);

protected:
	DesktopFrame(DesktopSize size,
		int stride,
		uint8_t* data,
		SharedMemory* shared_memory);

	uint8_t* const data_;
	SharedMemory* const shared_memory_;

private:
	const DesktopSize size_;
	const int stride_;

	DesktopRegion updated_region_;
	DesktopVector top_left_;
	DesktopVector dpi_;
	int64_t capture_time_ms_;
	uint32_t capturer_id_;

	DesktopFrame(const DesktopFrame&) = delete;
	DesktopFrame& operator=(const DesktopFrame&) = delete;
};

class BasicDesktopFrame : public DesktopFrame {
public:
	explicit BasicDesktopFrame(DesktopSize size);

	~BasicDesktopFrame() override;

	static DesktopFrame* CopyOf(const DesktopFrame& frame);

private:
	BasicDesktopFrame(const BasicDesktopFrame&) = delete;
	BasicDesktopFrame& operator=(const BasicDesktopFrame&) = delete;
};

class SharedMemoryDesktopFrame : public DesktopFrame {
public:
	static std::unique_ptr<DesktopFrame> Create(
		DesktopSize size,
		SharedMemoryFactory* shared_memory_factory);

	SharedMemoryDesktopFrame(DesktopSize size,
		int stride,
		SharedMemory* shared_memory);

	SharedMemoryDesktopFrame(DesktopSize size,
		int stride,
		std::unique_ptr<SharedMemory> shared_memory);

	~SharedMemoryDesktopFrame() override;

private:
	SharedMemoryDesktopFrame(DesktopRect rect,
		int stride,
		SharedMemory* shared_memory);

	SharedMemoryDesktopFrame(const SharedMemoryDesktopFrame&) = delete;
	SharedMemoryDesktopFrame& operator=(const SharedMemoryDesktopFrame&) = delete;
};

